/**
 * 队列：先进先出、后进后出
 */

#include "MessageQueue.h"

MessageQueue::MessageQueue() : MessageQueue(INT16_MAX) {

}

MessageQueue::MessageQueue(int32_t maxSize) :
    _pFirst(nullptr),
    _pLast(nullptr),
    _NbSize(0),
    _MaxSize(maxSize),
    _AbortRequest(false) {

}

MessageQueue::~MessageQueue() noexcept {
  flush();
}

void MessageQueue::flush() {
  MessageNode *curNode;
  MessageNode *nextNode;
  Message *pMsg;

  UniqueLock uniqueLock(event.getMutex(), std::defer_lock);
  event.lock(uniqueLock);
  for (curNode = _pFirst; curNode != nullptr; curNode = nextNode) {
    nextNode = curNode->pNext;
    pMsg = curNode->pMsg;
    DELETE_PTR(pMsg)
    DELETE_PTR(curNode)
  }
  event.unlock(uniqueLock);
}

int32_t MessageQueue::getSize() {
  UniqueLock uniqueLock(event.getMutex());
  int32_t size = _NbSize;
  return size;
}

void MessageQueue::abort() {
  UniqueLock uniqueLock(event.getMutex());
  _AbortRequest = true;
}

bool MessageQueue::enqueueMessage(Message *pMsg) {
  if (_AbortRequest) {
    DELETE_PTR(pMsg)
    return false;
  }
  //创建消息节点
  auto pNode = new MessageNode();
  pNode->pMsg = pMsg;
  UniqueLock uniqueLock(event.getMutex(), std::defer_lock);
  event.lock(uniqueLock);
  if (_pLast == nullptr) {
    _pFirst = pNode;
  } else {
    _pLast->pNext = pNode;
  }
  _pLast = pNode;
  _NbSize++;
  event.notify(true);
  event.unlock(uniqueLock);

  return true;
}

Message *MessageQueue::dequeueMessage(const int32_t waitMills) {
  MessageNode *pNode;
  UniqueLock uniqueLock(event.getMutex(), std::defer_lock);
  event.lock(uniqueLock);
  while (!_AbortRequest) {
    pNode = _pFirst;
    if (pNode) {
      _pFirst = pNode->pNext;
      if (!_pFirst)
        _pLast = nullptr;

      _NbSize--;
      auto pMsg = pNode->pMsg;
      DELETE_PTR(pNode)
      event.unlock(uniqueLock);
      return pMsg;
    } else if (waitMills != IMMEDIATE) {
      event.wait(uniqueLock, std::chrono::milliseconds(waitMills));
    } else {
      break;
    }
  }
  event.unlock(uniqueLock);
  return nullptr;
}

const char *MessageQueue::getClassName() {
  return "MessageQueue";
}

void MessageQueue::toString() {
  LOG_TEST("MessageQueue(NbSize:%d,MaxSize:%d,AbortRequest:%d)", _NbSize, _MaxSize, _AbortRequest)
}
